home *** CD-ROM | disk | FTP | other *** search
- /* OS- and machine-dependent stuff for the 8250 asynch chip on a IBM-PC */
- /* hacked to support BIOS interaction for NET PC9801 ala JK1NNT by N3EUA */
-
- #include "config.h"
- #if !defined(PLUS)
-
- #include <stdio.h>
- #include "global.h"
- #include "asy.h"
- #include "8250.h"
- #include "iface.h"
-
- struct asy asy[ASY_MAX];
- unsigned nasy;
-
- unsigned h2ivec[ASYHANDLE_MAX] ;
- unsigned nhandlers ;
-
- struct ivec ivec[NIVECS] ;
-
- #ifdef PC9801
- int work, f_handle, jkintdos();
- #define mask 0x00ff
- #endif
-
- /* ASY interrupt handlers */
- #ifndef PC9801
- extern void asy0vec(),asy1vec(),asy2vec(),asy3vec(),asy4vec();
- void (*handle[])() = {asy0vec,asy1vec,asy2vec,asy3vec,asy4vec};
- #endif
-
- /* Initialize asynch port "dev" */
- int
- asy_init(dev,arg1,arg2,bufsize)
- int16 dev;
- char *arg1,*arg2; /* Attach args for address and vector */
- unsigned bufsize;
- {
- register unsigned base;
- register struct fifo *fp;
- register struct asy *ap;
- unsigned vec ;
- void (*getirq())();
- char i_state;
-
- ap = &asy[dev];
- ap->addr = htoi(arg1);
- ap->vec = htoi(arg2);
- /* Set up receiver FIFO */
- fp = &ap->fifo;
- if((fp->buf = malloc(bufsize)) == NULLCHAR){
- printf("asy%d: No space for rx buffer\r\n",dev);
- fflush(stdout);
- return -1;
- }
- fp->bufsize = bufsize;
- fp->wp = fp->rp = fp->buf;
- fp->cnt = 0;
-
- base = ap->addr;
- ap->urgent = NULLCHAR; /* For SLFP urgent data */
-
- #ifdef PC9801
- f_handle = auxopen();
- #else
- /* Purge the receive data buffer */
- (void)inportb(base+RBR);
-
- i_state = disable();
-
- /* Set up interrupt vector structure if necessary */
- vec = ap->vec ;
- if (ivec[vec].ichain == NULLASY) {
- if (nhandlers == ASYHANDLE_MAX) {
- printf("asy%d: No more interrupt handlers\n",dev) ;
- return ;
- }
-
- /* Save original interrupt vector and mask */
- ivec[vec].oldvec = getirq(ap->vec) ;
- ivec[vec].oldmask = getmask(ap->vec) ;
-
- /* Set interrupt vector to SIO handler */
- setirq(ap->vec,handle[nhandlers]);
-
- /* Set up correspondence between handler and interrupt */
- h2ivec[nhandlers] = vec ;
-
- nhandlers++ ; /* step to next handler */
-
- /* Start chain */
- ivec[vec].ichain = &asy[dev] ;
- ap->ichain = NULLASY ; /* terminate the chain */
- }
- else { /* Already a handler for this; just put in chain */
- ap->ichain = ivec[vec].ichain ; /* Put at head */
- ivec[vec].ichain = &asy[dev] ;
- }
-
- /* Update vector reference count */
- ivec[vec].refcnt++ ;
-
- /* Save original control bits */
- ap->save.lcr = inportb(base+LCR);
- ap->save.ier = inportb(base+IER);
- ap->save.mcr = inportb(base+MCR);
-
- /* save speed bytes */
- setbit(base+LCR,LCR_DLAB);
- ap->save.divl = inportb(base+DLL);
- ap->save.divh = inportb(base+DLM);
- clrbit(base+LCR,LCR_DLAB);
-
- /* Set line control register: 8 bits, no parity */
- outportb(base+LCR,(char)LCR_8BITS);
-
- /* Turn on receive interrupt enable in 8250, leave transmit
- * and modem status interrupts turned off for now
- */
- outportb(base+IER,(char)IER_DAV);
-
- /* Set modem control register: assert DTR, RTS, turn on 8250
- * master interrupt enable (connected to OUT2)
- */
- outportb(base+MCR,(char)(MCR_DTR|MCR_RTS|MCR_OUT2));
-
- /* Enable interrupt */
- maskon(ap->vec);
- restore(i_state);
- #endif /* PC9801 */
- }
- int
- asy_stop(iface)
- struct interface *iface;
- {
- register unsigned base;
- register struct asy *ap;
- unsigned vec ;
- char i_state;
-
- ap = &asy[iface->dev];
- base = ap->addr;
-
- #ifndef PC9801
- /* Purge the receive data buffer */
- (void)inportb(base+RBR);
-
- /* See if this is the last asy using this interrupt, */
- /* and restore the vector and interrupt mask if it is */
- vec = ap->vec ;
- if (--ivec[vec].refcnt == 0) {
- i_state = disable();
- setirq(ap->vec,ivec[vec].oldvec);
- if(ivec[vec].oldmask)
- maskon(ap->vec);
- else
- maskoff(ap->vec);
- restore(i_state);
- }
-
- /* Restore original interrupt vector and 8259 mask state */
- /* Restore speed regs */
- setbit(base+LCR,LCR_DLAB);
- outportb(base+DLL,ap->save.divl); /* Low byte */
- outportb(base+DLM,ap->save.divh); /* Hi byte */
- clrbit(base+LCR,LCR_DLAB);
-
- /* Restore control regs */
- outportb(base+LCR,ap->save.lcr);
- outportb(base+IER,ap->save.ier);
- outportb(base+MCR,ap->save.mcr);
- #endif /* PC9801 */
- }
- /* Asynchronous line I/O control */
- asy_ioctl(interface,argc,argv)
- struct interface *interface;
- int argc;
- char *argv[];
- {
- if(argc < 1){
- printf("%d\r\n",asy[interface->dev].speed);
- return 0;
- }
- return asy_speed(interface->dev,atoi(argv[0]));
- }
- /* Set asynch line speed */
- int
- asy_speed(dev,speed)
- int16 dev;
- int speed;
- {
- #ifdef PC9801
- register int s_code, speed1;
-
- if (speed ==0 || speed >= 10000 || dev >= nasy)
- return -1;
-
- speed1 = 75;
- for (s_code = 0; speed >= speed1; s_code++) speed1 *= 2;
- speed1 /= 2;
- asy[dev].speed = speed1;
- set_speed(s_code);
- #else
- register unsigned base;
- register int divisor;
- char i_state;
-
- if(speed == 0 || dev >= nasy)
- return -1;
-
- base = asy[dev].addr;
- asy[dev].speed = speed;
-
- divisor = BAUDCLK / (long)speed;
-
- i_state = disable();
-
- /* Purge the receive data buffer */
- (void)inportb(base+RBR);
-
- /* Turn on divisor latch access bit */
- setbit(base+LCR,LCR_DLAB);
-
- /* Load the two bytes of the register */
- outportb(base+DLL,(char)(divisor & 0xff)); /* Low byte */
- outportb(base+DLM,(char)((divisor >> 8) & 0xff)); /* Hi byte */
-
- /* Turn off divisor latch access bit */
- clrbit(base+LCR,LCR_DLAB);
-
- restore(i_state);
- return 0;
- #endif /* PC9801 */
- }
-
- /* Send a buffer to serial transmitter */
- asy_output(dev,buf,cnt)
- unsigned dev;
- char *buf;
- unsigned short cnt;
- {
- #ifdef PC9801
- int b_cnt, b_buf;
-
- if (dev >= nasy) return;
- for (b_cnt = 0; b_cnt < cnt; b_cnt++) {
- b_buf = buf[b_cnt];
- aput(b_buf);
- }
- #else
- register struct dma *dp;
- unsigned base;
- char i_state;
-
- if(dev >= nasy)
- return;
- base = asy[dev].addr;
- dp = &asy[dev].dma;
- i_state = disable();
- if(dp->flags){
- restore(i_state);
- return; /* Already busy */
- }
- dp->data = buf;
- dp->cnt = cnt;
- dp->flags = 1;
- /* Enable transmitter buffer empty interrupt and simulate
- * an interrupt; this will get things rolling.
- */
- setbit(base+IER,IER_TxE);
- asytxint(dev);
- restore(i_state);
- #endif /* PC9801 */
- }
- /* Receive characters from asynch line
- * Returns count of characters read
- */
- int16
- asy_recv(dev,buf,cnt)
- int16 dev;
- char *buf;
- unsigned cnt;
- {
- unsigned tot,n;
- #ifdef PC9801
- int ch;
-
- for (tot = 0; tot < cnt; tot++) {
- if (auxstat()) ch = aget();
- else break;
- buf[tot] = ch;
- }
- return tot;
- }
-
- #else
- int kbread();
- char i_state;
- struct fifo *fp;
-
- fp = &asy[dev].fifo;
- tot = 0;
- /* Read from serial I/O input buffer */
- i_state = disable();
- for(;;){
- n = min(cnt,fp->cnt);
- if(n == 0)
- break;
- n = min(n,&fp->buf[fp->bufsize] - fp->rp);
- memcpy(buf,fp->rp,n);
- fp->rp += n;
- if(fp->rp >= &fp->buf[fp->bufsize])
- fp->rp = fp->buf;
- fp->cnt -= n;
- buf += n;
- tot += n;
- cnt -= n;
- }
- restore(i_state);
- return tot;
-
- }
- /* Interrupt handler for 8250 asynch chip */
- void
- asyint(handler)
- unsigned handler;
- {
- register unsigned base;
- register char iir;
- register unsigned dev ;
- struct asy *ap ;
- int someint ;
-
- /* The following bears some explaining. Because the PC and AT
- * (but not the PS/2) uses edge triggered interrupts, we need
- * to assure that the shared interrupt line makes a low-going
- * transition before we issue an EOI. If we don't, one of the
- * UARTs could raise its interrupt line again after we serviced
- * it, but before we cleared all the other UARTs' interrupts,
- * and this would be missed by the interrupt controller in
- * edge triggered mode. This should still work fine on a
- * PS/2.
- * Many thanks to Dan Dodge of Quantum Software, Ltd., for
- * suggesting this trick. dmf
- */
-
- do {
-
- someint = 0 ; /* No interrupt detected yet on this pass */
-
- ap = ivec[h2ivec[handler]].ichain ; /* Start at head of chain */
-
- while (ap != NULLASY) {
- dev = ap - asy ;
- base = asy[dev].addr;
- while(((iir = inportb(base+IIR)) & IIR_IP) == 0) {
- someint = 1 ; /* Detected an interrupt */
- switch(iir & IIR_ID){
- case IIR_RDA: /* Receiver interrupt */
- asyrxint(dev);
- break;
- case IIR_THRE: /* Transmit interrupt */
- asytxint(dev);
- break;
- }
- }
- ap = ap->ichain ; /* step to next in chain */
- }
-
- } while (someint != 0) ;
- }
- /* Process 8250 receiver interrupts */
- static
- asyrxint(dev)
- unsigned dev;
- {
- unsigned base;
- register struct fifo *fp;
- char c;
-
- base = asy[dev].addr;
- fp = &asy[dev].fifo;
- while(inportb(base+LSR) & LSR_DR){
- c = inportb(base+RBR);
- /* Process incoming data;
- * If buffer is full, we have no choice but
- * to drop the character
- */
- if(fp->cnt != fp->bufsize){
- *fp->wp++ = c;
- if(fp->wp == &fp->buf[fp->bufsize])
- /* Wrap around */
- fp->wp = fp->buf;
- fp->cnt++;
- }
- }
- }
- /* Handle 8250 transmitter interrupts */
- static
- asytxint(dev)
- unsigned dev;
- {
- register struct dma *dp;
- register unsigned base;
- unsigned urg; /* urgent SLFP data, or == 256 */
-
- base = asy[dev].addr;
- dp = &asy[dev].dma;
- if(!dp->flags){
- /* "Shouldn't happen", but disable transmit
- * interrupts anyway
- */
- clrbit(base+IER,IER_TxE);
- return; /* Nothing to send */
- }
- while(inportb(base+LSR) & LSR_THRE){
- /* Send any pending urgent data */
- if (asy[dev].urgent != NULLCHAR)
- if ((urg = (*asy[dev].urgent)(dev)) < 256) {
- outportb(base+THR,urg);
- continue;
- }
- dp->last_octet = *dp->data;
- outportb(base+THR,*dp->data++);
- if(--dp->cnt == 0){
- dp->flags = 0;
- /* Disable transmit interrupts */
- clrbit(base+IER,IER_TxE);
- /* Call completion interrupt here */
- break;
- }
- }
- }
-
- /* Set bit(s) in I/O port */
-
- setbit(port,bits)
- unsigned port;
- char bits;
- {
- outportb(port,(char)inportb(port)|bits);
- }
- /* Clear bit(s) in I/O port */
- clrbit(port,bits)
- unsigned port;
- char bits;
- {
- outportb(port,(char)(inportb(port) & ~bits));
- }
- #endif /* PC9801 */
-
- int
- stxrdy(dev)
- int16 dev;
- {
- return(!asy[dev].dma.flags);
- }
-
- #ifdef PC9801
- auxopen()
- {
- char *device;
- device = "AUX";
- work = jkintdos(0x3d02,0,device);
- return (work);
- }
-
- auxstat()
- {
- work = jkintdos(0x4406,f_handle,0) & mask;
- return (work);
- }
-
- aget()
- {
- work = jkintdos(0x0300,0,0) & mask;
- return (work);
- }
-
- aput(ch)
- int ch;
- {
- jkintdos(0x0400,0,ch);
- }
- #endif /* PC9801 */
- #endif /* !defined(PLUS) */
-